package block;

import beans.BlockBean;
import beans.BlockchainBean;
import beans.Student;
import utils.FileIsExist;

import java.io.*;
import java.util.ArrayList;

/**
 * @author:zhengyilong
 * @email:1797344574@qq.com
 * @phone:13479394730
 * @desc:
 * @DateTime:2022/5/14 11:26
 **/
public class Blockchain{

    //private static String path = "D:\\MyMaven\\publicChainSystem\\src\\main\\java\\blockchain.ser";
    private static String path = "./blockchain.ser";

    /**
     * 该方法用于创建一个带有创世区块的区块链
     * @param data 交易的数据
     */
    public static void NewBlockchain(String data) {
        boolean isExist = FileIsExist.fileIsExist(path);
        if (isExist){
            throw new Error("区块链已存在，不可重复创建");
        }
        //创建创世区块
        BlockBean blockBean = Block.NewGenesisBlock(data);
        //将创世区块存入区块链中
//        ArrayList<BlockBean> blockchains = new ArrayList<>();
//        blockchains.add(blockBean);
//        BlockchainBean blockchainBean = new BlockchainBean();
//        blockchainBean.setBlocks(blockchains);
        FirstSerializableWrite(blockBean);
        //将带有创世区块的区块链返回
        //return null;
    }

    /**
     * 该方法用于向区块链中添加区块
     * @param data
     */
    public static void AddBlock(String data) {
        //获取到区块链
        //ArrayList<BlockBean> blocks = blockchainBean.getBlocks();
        ArrayList<BlockBean> blocks = GetAllBlock();
        //获取到最后一个区块的哈希值
        //byte[] prevBlock = blocks.get(blocks.size() - 1).getHash();
        byte[] prevBlockHash = blocks.get(blocks.size() - 1).getHash();
        //创建区块
        BlockBean blockBean = Block.NewBlock(data, prevBlockHash);
        //将区块添加进区块链中
        //blocks.add(blockBean);
        AddBlockSerializableWrite(blockBean);
    }

    //第一次序列化写入区块，创建区块链的时候使用
    /**如果使用追加，即new文件的时候加上一个true，允许追加，就会以换一行的方式追加
     * 但是读取文件的时候因为是使用while循环，循环条件是判断下一个是否为空
     * 变会导致只读到第一次追加的区块信息
     * 所以在此不使用追加
    */
    private static void FirstSerializableWrite(BlockBean block){
        ObjectOutputStream oos = null;
        try {
            oos= new ObjectOutputStream(new FileOutputStream(path));

            oos.writeObject(block);
            oos.writeObject(null);
            oos.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (oos!=null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //序列化区块并写入文件，添加区块的时候使用
    /**
     * 因为没有办法追加，所以每一次添加区块信息到文件时，我们采取覆盖
     * 先读取文件中所有的区块信息将其存储在list集合中
     * 然后开始覆盖写，先将list集合中的区块依次存入文件内
     * 最后将新增的区块添加进文件中
     */
    private static void AddBlockSerializableWrite(BlockBean block){
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(path));
            Object object = ois.readObject();
            ArrayList<Object> objects = new ArrayList<>();
            while (object != null){
                //System.out.println(object);
                objects.add(object);
                object = ois.readObject();
            }

            oos= new ObjectOutputStream(new FileOutputStream(path));
            for (Object o : objects) {
                oos.writeObject(o);
            }
            oos.writeObject(block);
            oos.writeObject(null);
            oos.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (oos!=null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //从文件中获取到所有的区块信息，并将其装入集合中返回
    public static ArrayList<BlockBean> GetAllBlock(){
        ArrayList<BlockBean> blocks = new ArrayList<>();
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(path));
            BlockBean block = (BlockBean) ois.readObject();
            while (block != null){
                blocks.add(block);
                block = (BlockBean) ois.readObject();
            }
        }catch (Exception e){
//            System.out.println("end");
            //e.printStackTrace();
            throw new Error("没有区块链");
        }finally {
            if (ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return blocks;
    }
}

